#include <xml/node.h>
#include <xml/impl.h>
#include <cassert>
#include <memory>
#include <algorithm>
#include <boost/mem_fn.hpp>

namespace xml {

// const_node

const_node::const_node(const const_node & n)
	: _impl(0), _handle(0)
{
	if (!n.null()) {
		_impl = n._impl;
		_handle = _impl->copy(n._handle);
	}
}

const_node::~const_node()
{
	if (!null())
		_impl->release(_handle);
}

const_node & const_node::operator = (const const_node & n)
{
	if (!null()) {
		_impl->release(_handle);
		_impl = 0; _handle = 0;
	}
	if (!n.null()) {
		_impl = n._impl;
		_handle = _impl->copy(n._handle);
	}
	return * this;
}

string const_node::tag() const
{
	return null() ? "" : _impl->get_tag(_handle);
}

bool const_node::null() const
{
	return _impl != 0 ? _impl->null(_handle) : true;
}

void const_node::attributes(string_list & list) const
{
	if (null())
		return;
	std::auto_ptr<attribute_enumeration> pe(_impl->get_attributes(_handle));
	assert(pe.get() != 0);
	for (pe->first(); !pe->end(); pe->next()) 
	{
		list.push_back(pe->current());
	}
}

bool const_node::get_attr(const string & name, string & value) const
{
	if (null())
		return false;
	return _impl->get_attribute(_handle, name, value);
}

void const_node::children(const_node_list & nodes, filter f) const
{
	if (null())
		return;
	std::auto_ptr<node_enumeration> pe(
		f.tag().empty() ?
			_impl->get_children(_handle) : _impl->get_children_by_tag(_handle, f.tag()));
	assert(pe.get() != 0);
	for (pe->first(); !pe->end(); pe->next()) 
	{
		const_node n(_impl, pe->current());
		if (f.accept(n))
			nodes.push_back(n);
	}
}

void const_node::children(const_node_vector & nodes, filter f) const
{
	if (null())
		return;
	std::auto_ptr<node_enumeration> pe(
		f.tag().empty() ?
			_impl->get_children(_handle) : _impl->get_children_by_tag(_handle, f.tag()));
	assert(pe.get() != 0);
	for (pe->first(); !pe->end(); pe->next()) 
	{
		const_node n(_impl, pe->current());
		if (f.accept(n))
			nodes.push_back(n);
	}
}

const_node const_node::child(filter f) const
{
	const_node_list nodes;
	children(nodes, f);
	if (nodes.size() == 0)
		throw not_found(f);
	if (nodes.size() == 1)
		return nodes.front();
	throw ambiguous(f);
}

const_node const_node::child(filter f, const_node def) const
{
	const_node_list nodes;
	children(nodes, f);
	if (nodes.size() == 0)
		return def;
	if (nodes.size() == 1)
		return nodes.front();
	throw ambiguous(f);
}

// node

void node::set_attr(const string & name, const string & value) const
{
	if (null())
		return;
	_impl->set_attribute(_handle, name, value);
}

void node::children(node_list & nodes, filter f) const
{
	if (null())
		return;
	std::auto_ptr<node_enumeration> pe(
		f.tag().empty() ?
			_impl->get_children(_handle) : _impl->get_children_by_tag(_handle, f.tag()));
	assert(pe.get() != 0);
	for (pe->first(); !pe->end(); pe->next()) 
	{
		node n(_impl, pe->current());
		if (f.accept(n))
			nodes.push_back(n);
	}
}

void node::children(node_vector & nodes, filter f) const
{
	if (null())
		return;
	std::auto_ptr<node_enumeration> pe(
		f.tag().empty() ?
			_impl->get_children(_handle) : _impl->get_children_by_tag(_handle, f.tag()));
	assert(pe.get() != 0);
	for (pe->first(); !pe->end(); pe->next()) 
	{
		node n(_impl, pe->current());
		if (f.accept(n))
			nodes.push_back(n);
	}
}

node node::child(filter f, bool create_if_not_found) const
{
	node_list nodes;
	children(nodes, f);
	if (nodes.size() == 0) {
		if (create_if_not_found)
			return create(f.tag());
		else
			throw not_found(f);
	}
	if (nodes.size() == 1)
		return nodes.front();
	throw ambiguous(f);
}

node node::child(filter f, node def) const
{
	node_list nodes;
	children(nodes, f);
	if (nodes.size() == 0)
		return def;
	if (nodes.size() == 1)
		return nodes.front();
	throw ambiguous(f);
}

node node::create(const string & tag) const
{
	if (null())
		return node();
	return node(_impl, _impl->create_child(_handle, tag));
}

void node::assign(const const_node & n)
{
	clear(true, true);
	string_list aa;
	n.attributes(aa);
	for (string_list::iterator it = aa.begin(); it != aa.end(); ++it)
		attr(* it, n.attr<string>(* it));
	const_node_vector cc;
	n.children(cc);
	for (size_type i = 0; i < cc.size(); ++i)
		create(cc[i].tag()).assign(cc[i]);
}

node node::replace(const string & tag) const
{
	node_vector nodes;
	children(nodes, filter(tag));
	std::for_each(nodes.begin(), nodes.end(), boost::mem_fn(&node::erase));
	return create(tag);
}

void node::clear(bool attr, bool children) const
{
	if (null())
		return;
	_impl->clear(_handle, attr, children);
}

void node::erase()
{
	if (null())
		return;
	_impl->erase(_handle);
	_impl = 0; _handle = 0;
}

} // xml